import {InstanceType} from "typegoose";
import {InternalError} from "../../api/error";
import {DEFAULT_DELIVERY_FEE, DEFAULT_DELIVERY_FEE_THRESHOLD} from "../../api/prize";
import {AddressModel} from "../../database/models/address";
import {DeliveryModel} from "../../database/models/delivery";
import {DeliveryOrder, DeliveryOrderModel} from "../../database/models/deliveryOrder";
import {GameActivityModel} from "../../database/models/gameActivity";
import {GamePrize, GamePrizeModel} from "../../database/models/gamePrize";
import {LipstickSeries, LipstickSeriesModel} from "../../database/models/lipstickSeries";
import {PaymentOrderModel} from "../../database/models/paymentOrder";
import {PlayerModel} from "../../database/models/player";
import {Product, ProductModel} from "../../database/models/Product";
import {PressGroup} from "../../database/models/ProductGroup";
import {wechatPayAuther} from "../../wechat/wechatCashier";
import {expect, requestToApiServer, setupTestEnv, tearDownTestEnv, TestEnv, XMLbuilder} from "./helper";

describe('奖品API', () => {

  let pressGroup: InstanceType<PressGroup> = null
  let env: TestEnv = null
  before(async () => {
    env = await setupTestEnv()

    pressGroup = env.pressGroup
  })
  after(async () => {
    await tearDownTestEnv()
  })

  context('无奖品', () => {

    it('获取', () => {

      return requestToApiServer()
        .get('/player/prize')
        .set({'x-jwt': env.jwt})
        .expect(200)
        .then(({body}) => {

          expect(body).to.have.properties({
            ok: true, data: {
              prizes: []
            }
          })
        })
    });
  })
  context('口红机有奖品', () => {
    let address = null
    let prizes = null
    let mostStockProduct = null
    let otherStockProduct = null

    beforeEach(async () => {

      const ps = env.lipstickSeries.products as Array<InstanceType<Product>>
      const products = [] as Array<InstanceType<Product>>
      for (const p of ps) {
        const product = await ProductModel.findById(p._id)
        if (product) {
          products.push(product)
        }
      }
      products.sort((a, b) => {
        return b.stock - a.stock
      })
      mostStockProduct = products[0]
      otherStockProduct = products[1]

      const {body} = await requestToApiServer()
        .post(`/game/start/${env.lipstickSeries.id}`)
        .set({'x-jwt': env.jwt})
        .expect(200)
        .send()

      const act = body.data.activity

      await GameActivityModel.updateOne({_id: act}, {$set: {canWin: true}})

      await requestToApiServer()
        .post(`/game/claim/${act}`)
        .set({'x-jwt': env.jwt})
        .expect(200)
        .send({win: true, stage: 2})
        .then(({body}) => {
          expect(body).to.have.properties({data: {win: true}})
        })

      await requestToApiServer()
        .post('/player/address')
        .set({'x-jwt': env.jwt})
        .send({
          pcas: '330108003',
          address: '保亿大厦2222室',
          receiver: 'pshu',
          phone: '18688887777',
          alias: '公司2',
          default: true
        })
        .expect(200)
        .then(({body}) => {
          address = body.data.address
        })

      await requestToApiServer()
        .get('/player/prize')
        .set({'x-jwt': env.jwt})
        .expect(200)
        .then(({body}) => {
          prizes = body.data.prizes
        })

    })

    afterEach(async () => {
      await GameActivityModel.deleteMany({})
      await AddressModel.deleteMany({})
      await GamePrizeModel.deleteMany({})
    })

    it('获取所有奖品和可选择色号信息', async () => {

      return await requestToApiServer()
        .get('/player/prize')
        .set({'x-jwt': env.jwt})
        .then(async ({body}) => {
          expect(body).to.have.properties({
            ok: true,
            data: {prizes: [{product: mostStockProduct.id}]}
          })

          expect(body.data.prizes[0].selectList[0]).to.have.property(`selected`)

          for (const it of body.data.prizes[0].selectList) {
            const p = await ProductModel.findById(it._id)
            expect(it.stock).to.equal(p.stock)
          }
        })
    })

    it('某个奖品请求选色号', async () => {

      return requestToApiServer()
        .post(`/player/prize/select/${prizes[0]._id}`)
        .set({'x-jwt': env.jwt})
        .send({productId: otherStockProduct._id})
        .then(({body}) => {
          expect(body).to.have.properties({
            ok: true,
          })
        })
    })

    it('获取邮费', async () => {

      let sum = 0
      for (const it of prizes) {
        const pr: InstanceType<GamePrize> = await GamePrizeModel.findOne({
          _id: it._id,
          player: env.player._id
        })

        const p: InstanceType<Product> = await ProductModel.findOne({
          _id: pr.product
        })
        sum += p.marketPrice
      }

      const fee = sum >= DEFAULT_DELIVERY_FEE_THRESHOLD ? 0 : DEFAULT_DELIVERY_FEE

      await requestToApiServer()
        .post(`/player/prize/deliver/fee`)
        .set({'x-jwt': env.jwt})
        .send({
          prizeIds: [
            prizes[0]._id
          ]
        })
        .then(({body}) => {
          expect(body).to.have.properties({
            ok: true,
            data: {fee}
          })
        })
    })

    it('申请发货——prizes(免邮费)', async () => {

      const prize = await GamePrizeModel.findById(prizes[0]._id).populate(`product`).lean()
      await ProductModel.findOneAndUpdate({_id: prize.product._id}, {$set: {marketPrice: 1000}})

      await requestToApiServer()
        .post(`/player/prize/deliver/delivery`)
        .set({'x-jwt': env.jwt})
        .send({toAddress: address._id, prizeIds: [prizes[0]._id]})
        .then(({body}) => {
          expect(body).to.have.properties({
            ok: true,
          })
        })

      const delivery = await DeliveryModel.findOne({player: env.player._id})
      expect(delivery).to.have.properties({state: "prepared"})

    })

    it('申请发货——prizes(收邮费)  微信', async () => {
      let deliveryOrder: InstanceType<DeliveryOrder> = null
      const prize = await GamePrizeModel.findById(prizes[0]._id).populate(`product`).lean()
      await ProductModel.findOneAndUpdate({_id: prize.product._id}, {$set: {marketPrice: 1}})

      await requestToApiServer()
        .post(`/player/prize/deliver/delivery`)
        .set({'x-jwt': env.jwt})
        .send({toAddress: address._id, prizeIds: [prizes[0]._id], payType: 'wechatPay'})
        .then(({body}) => {
          deliveryOrder = body.data.deliveryOrder
        })

      const payOrder = await PaymentOrderModel.findOne({
        orderType: 'DeliveryOrder', order: deliveryOrder._id
      })

      const notification = {
        out_trade_no: payOrder.id,
        result_code: 'SUCCESS', return_code: 'SUCCESS',
      }

      const sign = wechatPayAuther.sign(notification)

      const xml = XMLbuilder.buildObject({...notification, sign})

      const res = await requestToApiServer()
        .post('/wechatpay/notify')
        .type('xml')
        .send(xml)
        .expect(200)
        .then((res) => {
          expect(res.text).to.equal('<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>')
        })

      expect(await PaymentOrderModel.findOne({
        orderType: 'DeliveryOrder', order: deliveryOrder._id
      })).to.have.properties({state: 'finished'})

      const delivery = await DeliveryModel.findOne({player: env.player._id})
      expect(delivery).to.have.properties({state: "prepared"})

      expect(await DeliveryOrderModel.findById(deliveryOrder._id).lean())
        .to.have.properties({state: 'paid'})
    })

    it('申请发货——prizes(收邮费) 支付宝', async () => {
      const prize = await GamePrizeModel.findById(prizes[0]._id).populate(`product`).lean()
      await ProductModel.findOneAndUpdate({_id: prize.product._id}, {$set: {marketPrice: 1}})

      await requestToApiServer()
        .post(`/player/prize/deliver/delivery`)
        .set({'x-jwt': env.jwt})
        .send({toAddress: address._id, prizeIds: [prizes[0]._id], payType: 'aliPay'})
        .expect(200)
    })


    it('申请发货——（要邮费）退出支付选择其他支付方式', async () => {
      let deliveryOrder: InstanceType<DeliveryOrder> = null
      const prize = await GamePrizeModel.findById(prizes[0]._id).populate(`product`).lean()
      await ProductModel.findOneAndUpdate({_id: prize.product._id}, {$set: {marketPrice: 1}})

      await requestToApiServer()
        .post(`/player/prize/deliver/delivery`)
        .set({'x-jwt': env.jwt})
        .send({toAddress: address._id, prizeIds: [prizes[0]._id], payType: 'aliPay'})
        .expect(200)

      await requestToApiServer()
        .post(`/player/prize/deliver/delivery`)
        .set({'x-jwt': env.jwt})
        .send({toAddress: address._id, prizeIds: [prizes[0]._id], payType: 'wechatPay'})
        .then(({body}) => {
          deliveryOrder = body.data.deliveryOrder
        })
      const payOrder = await PaymentOrderModel.findOne({
        orderType: 'DeliveryOrder', order: deliveryOrder._id
      })

      const notification = {
        out_trade_no: payOrder.id,
        result_code: 'SUCCESS', return_code: 'SUCCESS',
      }

      const sign = wechatPayAuther.sign(notification)

      const xml = XMLbuilder.buildObject({...notification, sign})

      const res = await requestToApiServer()
        .post('/wechatPay/notify')
        .type('xml')
        .send(xml)
        .expect(200)
        .then((res) => {
          expect(res.text).to.equal('<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>')
        })

      expect(await PaymentOrderModel.findOne({
        orderType: 'DeliveryOrder', order: deliveryOrder._id
      })).to.have.properties({state: 'finished'})

      const delivery = await DeliveryModel.findOne({player: env.player._id})
      expect(delivery).to.have.properties({state: "prepared"})

      expect(await DeliveryOrderModel.findById(deliveryOrder._id).lean())
        .to.have.properties({state: 'paid'})
    })

    it('申请发货(验证第三者批量请求同一id时不处理请求)', async () => {

      await requestToApiServer()
        .post(`/player/prize/deliver/delivery`)
        .set({'x-jwt': env.jwt})
        .send({
          toAddress: address._id,
          prizeIds: [prizes[0]._id,
            prizes[0]._id, prizes[0]._id],
          payType: 'wechatPay'
        })
        .then(({body}) => {
          expect(body).to.have.properties({
            ok: false,
          })
        })
    })
  })
  context('按的准有奖品', () => {
    let address = null
    let prizes = null
    let mostStockProduct = null
    let otherStockProduct = null

    beforeEach(async () => {
      const lss = env.pressGroup.level3 as Array<InstanceType<LipstickSeries>>
      const lipstickSerieses = [] as Array<InstanceType<LipstickSeries>>
      for (const it of lss) {
        const series = await LipstickSeriesModel.findById(it._id)
        if (!series)
          throw InternalError("NO_SUCH_SERIES")
        lipstickSerieses.push(series)
      }
      // lipstickSerieses.sort((a, b) => {
      //   return b.stock - a.stock
      // })
      const ps = lipstickSerieses[0].products as Array<InstanceType<Product>>
      const products = [] as Array<InstanceType<Product>>
      for (const p of ps) {
        const product = await ProductModel.findById(p._id)
        if (!product)
          throw InternalError("NO_SUCH_PRODUCT")
        products.push(product)
      }
      products.sort((a, b) => {
        return b.stock - a.stock
      })
      mostStockProduct = products[0]
      otherStockProduct = products[1]

      const {body} = await requestToApiServer()
        .post(`/game/pressAccurateStart/${pressGroup._id}`)
        .set({'x-jwt': env.jwt})
        .expect(200)
        .send()

      const act = body.data.activity

      await GameActivityModel.updateOne({_id: act}, {$set: {winLevel: 3}})

      await requestToApiServer()
        .post(`/game/pressAccurateClaim/${act}`)
        .set({'x-jwt': env.jwt})
        .expect(200)
        .send({winLevel: 3})
        .then(({body}) => {
          expect(body).to.have.properties({data: {winLevel: 3}})
        })

      await requestToApiServer()
        .post('/player/address')
        .set({'x-jwt': env.jwt})
        .send({
          pcas: '330108003',
          address: '保亿大厦2222室',
          receiver: 'pshu',
          phone: '18688887777',
          alias: '公司2',
          default: true
        })
        .expect(200)
        .then(({body}) => {
          address = body.data.address
        })

      await requestToApiServer()
        .get('/player/prize')
        .set({'x-jwt': env.jwt})
        .expect(200)
        .then(({body}) => {
          prizes = body.data.prizes
        })

    })

    afterEach(async () => {
      await GameActivityModel.deleteMany({})
      await AddressModel.deleteMany({})
      await GamePrizeModel.deleteMany({})
    })

    it('获取所有奖品和可选择色号信息', async () => {

      return requestToApiServer()
        .get('/player/prize')
        .set({'x-jwt': env.jwt})
        .then(async ({body}) => {
          expect(body).to.have.properties({
            ok: true,
            data: {prizes: [{product: mostStockProduct.id}]}
          })

          expect(body.data.prizes[0].selectList[0]).to.have.property(`selected`)

          for (const it of body.data.prizes[0].selectList) {
            const p = await ProductModel.findById(it._id)
            expect(it.stock).to.equal(p.stock)
          }
        })
    })

    it('某个奖品请求选色号', async () => {

      return requestToApiServer()
        .post(`/player/prize/select/${prizes[0]._id}`)
        .set({'x-jwt': env.jwt})
        .send({productId: otherStockProduct._id})
        .then(({body}) => {
          expect(body).to.have.properties({
            ok: true,
          })
        })
    })

    it('获取邮费', async () => {

      let sum = 0
      for (const it of prizes) {
        const pr: InstanceType<GamePrize> = await GamePrizeModel.findOne({
          _id: it._id,
          player: env.player._id
        })

        const p: InstanceType<Product> = await ProductModel.findOne({
          _id: pr.product
        })
        sum += p.marketPrice
      }

      const fee = sum >= DEFAULT_DELIVERY_FEE_THRESHOLD ? 0 : DEFAULT_DELIVERY_FEE

      await requestToApiServer()
        .post(`/player/prize/deliver/fee`)
        .set({'x-jwt': env.jwt})
        .send({
          prizeIds: [
            prizes[0]._id
          ]
        })
        .then(({body}) => {
          expect(body).to.have.properties({
            ok: true,
            data: {fee}
          })
        })
    })

    it('申请发货——prizes(免邮费)', async () => {

      const prize = await GamePrizeModel.findById(prizes[0]._id).populate(`product`).lean()
      await ProductModel.findOneAndUpdate({_id: prize.product._id}, {$set: {marketPrice: 1000}})

      await requestToApiServer()
        .post(`/player/prize/deliver/delivery`)
        .set({'x-jwt': env.jwt})
        .send({toAddress: address._id, prizeIds: [prizes[0]._id]})
        .then(({body}) => {
          expect(body).to.have.properties({
            ok: true,
          })
        })

      const delivery = await DeliveryModel.findOne({player: env.player._id})
      expect(delivery).to.have.properties({state: "prepared"})

    })

    it('申请发货——prizes(收邮费)  微信', async () => {
      let deliveryOrder: InstanceType<DeliveryOrder> = null
      const prize = await GamePrizeModel.findById(prizes[0]._id).populate(`product`).lean()
      await ProductModel.findOneAndUpdate({_id: prize.product._id}, {$set: {marketPrice: 1}})

      await requestToApiServer()
        .post(`/player/prize/deliver/delivery`)
        .set({'x-jwt': env.jwt})
        .send({toAddress: address._id, prizeIds: [prizes[0]._id], payType: 'wechatPay'})
        .then(({body}) => {
          deliveryOrder = body.data.deliveryOrder
        })

      const payOrder = await PaymentOrderModel.findOne({
        orderType: 'DeliveryOrder', order: deliveryOrder._id
      })

      const notification = {
        out_trade_no: payOrder.id,
        result_code: 'SUCCESS', return_code: 'SUCCESS',
      }

      const sign = wechatPayAuther.sign(notification)

      const xml = XMLbuilder.buildObject({...notification, sign})

      const res = await requestToApiServer()
        .post('/wechatpay/notify')
        .type('xml')
        .send(xml)
        .expect(200)
        .then((res) => {
          expect(res.text).to.equal('<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>')
        })

      expect(await PaymentOrderModel.findOne({
        orderType: 'DeliveryOrder', order: deliveryOrder._id
      })).to.have.properties({state: 'finished'})

      const delivery = await DeliveryModel.findOne({player: env.player._id})
      expect(delivery).to.have.properties({state: "prepared"})

      expect(await DeliveryOrderModel.findById(deliveryOrder._id).lean())
        .to.have.properties({state: 'paid'})
    })

    it('申请发货——prizes(收邮费) 支付宝', async () => {
      const prize = await GamePrizeModel.findById(prizes[0]._id).populate(`product`).lean()
      await ProductModel.findOneAndUpdate({_id: prize.product._id}, {$set: {marketPrice: 1}})

      await requestToApiServer()
        .post(`/player/prize/deliver/delivery`)
        .set({'x-jwt': env.jwt})
        .send({toAddress: address._id, prizeIds: [prizes[0]._id], payType: 'aliPay'})
        .expect(200)
    })


    it('申请发货——（要邮费）退出支付选择其他支付方式', async () => {
      let deliveryOrder: InstanceType<DeliveryOrder> = null
      const prize = await GamePrizeModel.findById(prizes[0]._id).populate(`product`).lean()
      await ProductModel.findOneAndUpdate({_id: prize.product._id}, {$set: {marketPrice: 1}})

      await requestToApiServer()
        .post(`/player/prize/deliver/delivery`)
        .set({'x-jwt': env.jwt})
        .send({toAddress: address._id, prizeIds: [prizes[0]._id], payType: 'aliPay'})
        .expect(200)

      await requestToApiServer()
        .post(`/player/prize/deliver/delivery`)
        .set({'x-jwt': env.jwt})
        .send({toAddress: address._id, prizeIds: [prizes[0]._id], payType: 'wechatPay'})
        .then(({body}) => {
          deliveryOrder = body.data.deliveryOrder
        })
      const payOrder = await PaymentOrderModel.findOne({
        orderType: 'DeliveryOrder', order: deliveryOrder._id
      })

      const notification = {
        out_trade_no: payOrder.id,
        result_code: 'SUCCESS', return_code: 'SUCCESS',
      }

      const sign = wechatPayAuther.sign(notification)

      const xml = XMLbuilder.buildObject({...notification, sign})

      const res = await requestToApiServer()
        .post('/wechatPay/notify')
        .type('xml')
        .send(xml)
        .expect(200)
        .then((res) => {
          expect(res.text).to.equal('<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>')
        })

      expect(await PaymentOrderModel.findOne({
        orderType: 'DeliveryOrder', order: deliveryOrder._id
      })).to.have.properties({state: 'finished'})

      const delivery = await DeliveryModel.findOne({player: env.player._id})
      expect(delivery).to.have.properties({state: "prepared"})

      expect(await DeliveryOrderModel.findById(deliveryOrder._id).lean())
        .to.have.properties({state: 'paid'})
    })

    it('申请发货(验证第三者批量请求同一id时不处理请求)', async () => {

      await requestToApiServer()
        .post(`/player/prize/deliver/delivery`)
        .set({'x-jwt': env.jwt})
        .send({
          toAddress: address._id,
          prizeIds: [prizes[0]._id,
            prizes[0]._id, prizes[0]._id],
          payType: 'wechatPay'
        })
        .then(({body}) => {
          expect(body).to.have.properties({
            ok: false,
          })
        })
    })
  })
  context('把奖品退货换成积分', () => {
    let address = null
    let prizes = null
    let mostStockProduct = null
    let otherStockProduct = null

    beforeEach(async () => {

      const ps = env.lipstickSeries.products as Array<InstanceType<Product>>
      const products = [] as Array<InstanceType<Product>>
      for (const p of ps) {
        const product = await ProductModel.findById(p._id)
        if (product) {
          products.push(product)
        }
      }
      products.sort((a, b) => {
        return b.stock - a.stock
      })
      mostStockProduct = products[0]
      otherStockProduct = products[1]

      const {body} = await requestToApiServer()
        .post(`/game/start/${env.lipstickSeries.id}`)
        .set({'x-jwt': env.jwt})
        .expect(200)
        .send()

      let act = body.data.activity

      await GameActivityModel.updateOne({_id: act}, {$set: {canWin: true}})

      await requestToApiServer()
        .post(`/game/claim/${act}`)
        .set({'x-jwt': env.jwt})
        .expect(200)
        .send({win: true, stage: 2})
        .then(({body}) => {
          expect(body).to.have.properties({data: {win: true}})
        })

      const res = await requestToApiServer()
        .post(`/game/start/${env.lipstickSeries.id}`)
        .set({'x-jwt': env.jwt})
        .expect(200)
        .send()

      act = res.body.data.activity

      await GameActivityModel.updateOne({_id: act}, {$set: {canWin: true}})

      await requestToApiServer()
        .post(`/game/claim/${act}`)
        .set({'x-jwt': env.jwt})
        .expect(200)
        .send({win: true, stage: 2})
        .then(({body}) => {
          expect(body).to.have.properties({data: {win: true}})
        })

      await requestToApiServer()
        .get('/player/prize')
        .set({'x-jwt': env.jwt})
        .expect(200)
        .then(({body}) => {
          prizes = body.data.prizes
        })

    })

    afterEach(async () => {
      await GameActivityModel.deleteMany({})
      await AddressModel.deleteMany({})
      await GamePrizeModel.deleteMany({})
    })

    it('退货成积分', async () => {

      const p00 = await ProductModel.findById(prizes[0].product)
      const p10 = await ProductModel.findById(prizes[1].product)
      const player0 = await PlayerModel.findById(env.player)
      let points = 0
      await requestToApiServer()
        .post('/player/prize/return/point')
        .set({'x-jwt': env.jwt})
        .send({prizeIds: [prizes[0]._id, prizes[1]._id]})
        .then(res => {
          points = res.body.data.points
        })

      const p01 = await ProductModel.findById(prizes[0].product)
      expect(p01.stock).to.equal(p00.stock + 1)
      const p11 = await ProductModel.findById(prizes[1].product)
      expect(p11.stock).to.equal(p10.stock + 1)
      const player1 = await PlayerModel.findById(env.player)
      expect(player1.point).to.equal(player0.point + points)
    })
  })
})
